import { promises as fs } from 'fs';
import { join, basename, dirname } from 'path';
import * as XLSX from 'xlsx';
export class JsonToExcelConverter {
    constructor(options = {}) {
        this.opts = {
            inputDir: options.inputDir ?? './json',
            outputFile: options.outputFile ?? './output.xlsx',
            sheetName: options.sheetName ?? 'Data',
            flatten: options.flatten ?? true,
            includeSubdirs: options.includeSubdirs ?? false,
            filePattern: options.filePattern ?? /\.json$/i,
            exportMode: options.exportMode ?? 'horizontal',
        };
    }
    /* 主入口 */
    async convert() {
        const data = this.opts.exportMode === 'vertical'
            ? await this.buildVertical()
            : await this.readJsonFiles();
        if (!data.length)
            throw new Error('No data to convert');
        return this.writeExcel(data);
    }
    /* =============== 横向模式（原逻辑） =============== */
    async readJsonFiles() {
        const files = await this.locateJson(this.opts.inputDir);
        const out = [];
        for (const f of files) {
            try {
                const raw = JSON.parse(await fs.readFile(f, 'utf8'));
                const arr = Array.isArray(raw) ? raw : [raw];
                const flattened = this.opts.flatten
                    ? arr.map((o) => this.flatten(o))
                    : arr;
                flattened.forEach((row) => out.push({ ...row, __source_file: basename(f) }));
            }
            catch (e) {
                console.warn(`Skip ${f}:`, e.message);
            }
        }
        return out;
    }
    /* =============== 竖向模式（i18n） =============== */
    async buildVertical() {
        const files = await this.locateJson(this.opts.inputDir);
        const langMap = {}; // lang -> flatObj
        const allKeys = new Set();
        for (const f of files) {
            const lang = basename(f).replace(this.opts.filePattern, ''); // en / zh
            const flat = this.flatten(await this.readJsonFile(f));
            langMap[lang] = flat;
            Object.keys(flat).forEach((k) => allKeys.add(k));
        }
        return Array.from(allKeys).map((k) => {
            const row = { key: k };
            Object.entries(langMap).forEach(([lang, map]) => {
                row[lang] = map[k] ?? '';
            });
            return row;
        });
    }
    /* 通用工具：递归找文件 */
    async locateJson(root) {
        const files = [];
        const walk = async (dir) => {
            for (const d of await fs.readdir(dir, { withFileTypes: true })) {
                const full = join(dir, d.name);
                if (d.isDirectory() && this.opts.includeSubdirs)
                    await walk(full);
                if (d.isFile() && this.opts.filePattern.test(d.name))
                    files.push(full);
            }
        };
        await walk(root);
        return files;
    }
    async readJsonFile(f) {
        return JSON.parse(await fs.readFile(f, 'utf8'));
    }
    /* 扁平化 */
    flatten(obj, prefix = '', res = {}) {
        if (obj === null || obj === undefined)
            return res;
        if (typeof obj !== 'object' || obj instanceof Date) {
            res[prefix] = obj;
            return res;
        }
        if (Array.isArray(obj)) {
            obj.forEach((v, i) => this.flatten(v, prefix ? `${prefix}[${i}]` : `[${i}]`, res));
            return res;
        }
        for (const [k, v] of Object.entries(obj)) {
            const key = prefix ? `${prefix}.${k}` : k;
            this.flatten(v, key, res);
        }
        return res;
    }
    /* 写 Excel */
    async writeExcel(data) {
        const wb = XLSX.utils.book_new();
        const ws = XLSX.utils.json_to_sheet(data);
        XLSX.utils.book_append_sheet(wb, ws, this.opts.sheetName);
        await fs.mkdir(dirname(this.opts.outputFile), { recursive: true });
        XLSX.writeFile(wb, this.opts.outputFile);
        return this.opts.outputFile;
    }
}
//# sourceMappingURL=converter.js.map